static void set_cursor_pos(uint ctrl_nr, uint row, uint col)
{
	struct lcdmod_lcd *lcd = &lcddev.lcd[ctrl_nr];
	row &= 0x1;
	if ( lcd->cur_row == row && lcd->cur_col == col ) return;
	lcd->cur_col = col;
	lcd->cur_row = row;
	PDEBUG_OPERATION(" set_cursor_pos ctrlnr: %d row/col:%2d/%2d ", ctrl_nr, row, col);
	write_inst(ctrl_nr, HD44780_DDRAM_ADDRES | ( row << 6 ) | col );
}


static void inc_virtual_cursor_pos(void)
{
	if ( col < (DISP_COLS - 1) ) {
		++col;
	} else if ( !test_bit(FLAG_WRAP_OFF, &lcddev.flags ) && 
			row < (DISP_ROWS - 1) ) {
		col = 0;
		++row;
	}
}

static void inc_ctrl_cursor_pos_on_operation(uint ctrl_nr, uint row, uint col)
{
	struct lcdmod_lcd *lcd = &lcddev.lcd[ctrl_nr];
	
	if (col < (DISP_COLS-1) ) {
		lcd->cur_col++;
	} else {
		lcd->cur_col = 0;
		/* (row++)&1 = (row^1)&1 */
		lcd->cur_row = (row&1)^1;
	}
}

static unsigned char _read_DDRAM(unsigned int row, unsigned int col)
{
	/* state_mutex is held */
	const u16 ctrl_nr = row / ROWS_PER_CTRL;
	u8 buff;
	
	PDEBUG_OPERATION("rDDRAM: row:%d per_ctr:%d col:%d per_ctr:%d stt: %c",
		row, ROWS_PER_CTRL, col, NUM_CONTROLLERS, state[row][col]);
	
	set_cursor_pos(ctrl_nr, row, col);
	buff = read_data(ctrl_nr);
	inc_ctrl_cursor_pos_on_operation(ctrl_nr, row, col);
	PDEBUG_OPERATION("rDDRAM:readed %2x %c ", buff, buff);
	
	/* reading the device sucks about cursor position
	 * refer to documentation. needed to fix this: */
	write_inst(ctrl_nr, HD44780_DDRAM_ADDRES | ( lcddev.lcd[ctrl_nr].cur_row << 6 ) | lcddev.lcd[ctrl_nr].cur_col );
	
	return buff;
}

static unsigned char read_DDRAM(unsigned int row, unsigned int col)
{
	unsigned char buff;
	mutex_lock(&state_mutex);
	buff = _read_DDRAM(row, col);
	mutex_unlock(&state_mutex);
	return(buff);
}

static unsigned char global_read_DDRAM(void)
{
	unsigned char buff;
	buff = read_DDRAM(row, col);
	inc_virtual_cursor_pos();
	return buff;
}

static void _write_DDRAM(unsigned int row, unsigned int col, unsigned char data)
{
	/* state_mutex is held */
	const uint ctrl_nr = row / ROWS_PER_CTRL;
	
	PDEBUG_OPERATION("wDDRAM: row:%d per_ctr:%d col:%d per_ctr:%d  data: %#2x %1c stt: %#2x %1c",
		row,ROWS_PER_CTRL,col, NUM_CONTROLLERS,  data, data, state[row][col], state[row][col]);
	
	state[row][col] = data;
	set_cursor_pos(ctrl_nr, row, col);
	write_data(ctrl_nr, data);
	inc_ctrl_cursor_pos_on_operation(ctrl_nr, row, col);
}

static void write_DDRAM(unsigned int row, unsigned int col, unsigned char data)
{
	/* check if we really need to write anything */
	mutex_lock(&state_mutex);
	if ( state[row][col] != data ) {
		_write_DDRAM(row, col, data);
	}
	mutex_unlock(&state_mutex);
}

static void global_write_DDRAM(unsigned char data)
{
	write_DDRAM(row, col, data);
	inc_virtual_cursor_pos();
}

/** write cgram to the specyfic controler on specified port */
static void write_CGRAM(unsigned int ctrl_nr, unsigned int cgram_index, unsigned char *cgram_pixels) 
{
	struct lcdmod_lcd *lcd = &lcddev.lcd[ctrl_nr];
	int i;
	
	PDEBUG_OPERATION("wCGRAM: :%d ctrl_nr:%d cgram_index:%d", ctrl_nr, cgram_index); 
	
	if( !memcmp(lcd->cgram[cgram_index], cgram_pixels, 8*sizeof(cgram_pixels)) ) 
		return;
	
	mutex_lock(&state_mutex);
	
	write_inst(ctrl_nr, HD44780_CGRAM_ADDRES | ( 8 * cgram_index ) );
	for (i = 0; i < 8; i++ ) {
		lcd->cgram[cgram_index][i] = cgram_pixels[i];
		write_data(ctrl_nr, cgram_pixels[ i ] );
	}
	inc_ctrl_cursor_pos_on_operation(ctrl_nr, lcd->cur_row, lcd->cur_col);
	
	mutex_unlock(&state_mutex);
}

/** write cgram to all lcds */
static void write_CGRAM_all(unsigned int cgram_index, unsigned char *cgram_pixels)
{
	int i;
	PDEBUG_OPERATION("wCGRAM_all: cgram_index:%d", cgram_index); 
	
	for (i = 0; i < NUM_CONTROLLERS; ++i) {
		write_CGRAM(i, cgram_index, cgram_pixels);
	}
}

/* initialize all controllers */
static void init_displays (void)
{
	int i;
#ifdef LOAD_CGRAM
	char cg[8][8] = DEFAULT_CGRAM;
#endif

	port_init();
	write_inst_all( HD44780_FS_DATAWIDTH8BIT |
			HD44780_FS_TWOLINES |
			HD44780_FS_5x8DOTS);
	write_inst_all( HD44780_DOOC_DISPLAYON |
			HD44780_DOOC_CURSOROFF |
			HD44780_DOOC_CURSORNOBLINK);
	write_inst_all( HD44780_CLRDISP);
	write_inst_all( HD44780_EMS_INC | HD44780_EMS_NOSHIFT);
	/* initialization ends */
	
	/*
	 * write cgram if it is so defined in config.h, config.h should
	 * include specyfic file with table cg[8][8] and function init_charmap() 
	 * This writes all 8 entire (5x8) characters to the CGRAM from cg[i]
	 */
	PDEBUG_OPERATION(" WRITING CGRAM \n");
	for(i = 0; i < 8; i++ ) {
#ifdef LOAD_CGRAM
		write_CGRAM_all(i, cg[i]);
#else
		write_CGRAM_all(i, "00000000");
#endif
	}
	PDEBUG_OPERATION("RET HOME\n");
	write_inst_all( HD44780_RETURNHOME);
}
